#include "CommandEngine.h"

/** Global variables **/
int running_flag = 1; /* 1 for "all is good", 0 for sudden exit */
FILE *nodes = NULL;
FILE *edges = NULL;
Network *global_net = NULL;

#define IS_VALUE_I(X)((1-x)<0.00001)
double *scores; /* array holds CPLEX results for each k, iteration 1 in cell 0 etc.*/
double *objval = NULL;
char *output_path = NULL; /* holds output folder path */



/* MAIN FUNCTION - calls and manages the following stages :*/
/* Stage 1: user input + network creation */
/* Stage 2: solving k_cluster using CPLEX */
/* Stage 3: creating output files */
/* Stage 4: memory free + exit */
int main(int argc, char **argv){

	int upper_bound, lower_bound, true_upper_bound ,number_of_nodes;
	int i, j;
	/*Vertex* tVertex;*/

	xmlDocPtr pXMLDoc;
	ClusterSet* currentSet = NULL;
	Cluster* tCluster = NULL;
	Edge* networkEdges = NULL;
	int networkEdgesSize;

	Vertex* networkVertexs = NULL;
	int networkVertexsSize;

	/* We use a special flag for a general state - 1 means "OK! coninue", 0 means "GET OUT AND FREE MEMORY*/
	running_flag = 1;


	/* ================================== */
	/* Stage 1 - build network from input */
	/* ================================== */

	/* Create/Initialize the network data structure */
	global_net = createNetwork();
	if(global_net==NULL){
		quit(global_net);
		return 0;
	}

	/* Open input files for reading, read input data and build network */
	running_flag = handle_user_input(argc, argv);
	if(running_flag == 0){
		quit(global_net);
		return 0;
	}

	/* Extract lower_bound & upper_bound from argv */
	running_flag = extract_bounds_from_argv(argv, &lower_bound, &upper_bound, &true_upper_bound);
	if(running_flag == 0){
		quit(global_net);
		return 0;
	}

	/* ======================================= */
	/* Stage 2 - solving k_cluster using CPLEX */
	/* ======================================= */

	/* a. Prepare special data structures */

	number_of_nodes = global_net->size;

	/* Prepare scores array - will hold CPLEX results which will be printed to the results file. */
	scores = (double*)malloc(sizeof(double)*(upper_bound-lower_bound + 3));
	running_flag = double_allocation_verifier(scores); /* verify creation */
	if(running_flag == 0){
		free(scores);
		quit(global_net);
		return 0;
	}

	/* Create networkEdges And networkEdgesSize From Network*/

	/* Prepare XGMML data structures */
	networkEdgesSize = global_net->totalEdgeNumber;
	running_flag = Create_Network_Edges_Data_Stractures(&networkEdges, &networkEdgesSize);
	if(running_flag == 0){
			free(scores);
			quit(global_net);
			return 0;
	}

	running_flag = CreateNetwork_Vertices_Data_Stractures(&networkVertexs, &networkVertexsSize);
	/* next if statement will deal with running flag != 0 situation */

	/* b. Call Create XMLDoc Single Time*/
	if(running_flag == 1)
		pXMLDoc = CreateXmlDocForNetwork(networkEdges,networkEdgesSize,networkVertexs,networkVertexsSize);

	/* c. run CPLEX, update results in data structures and create xgmml, best_clusters and results files */
	if(running_flag == 1){

		for(i=lower_bound; i<=upper_bound; i++){

			/* i vs. number of nodes check & action (number of clusters relative to number of nodes) */
			/* if lower_bound > number_of_nodes, create only results file, and exit program*/
			if(lower_bound > number_of_nodes) /* program will only produce results file with infeasible values */
				running_flag = create_results_file(scores, lower_bound, upper_bound);

			/* 1. As long as i < number of nodes, continue as "usual".*/
			/* 2. When i>number of nodes, solution is infeasible so no xgmml file is produced.
			 * Get out of this loop and finish program run. */
			if(i > number_of_nodes) break;

			/* Solve k_cluster problem for the current k=i */
			running_flag = solve_k_cluster_problem(i);

			/* =============================== */
			/* Stage 3 - create  output  files */
			/* =============================== */

			/* In case of an error in "solve_k_cluster_problem" function's run, do: */
			if(running_flag == 0){ /* CPLEX didn't solve problem for current k*/
				running_flag = 1; /* initialize flag, we're trying to continue with next k */
				running_flag = printf("Warning: failed to solve problem for k=%d.\nProgram will continue to next k\n", i);
				if(i<upper_bound){
					if(running_flag < 0){
						break; /* Exit loop here, and go on with ending program' run */
					}else{ /* CPLEX failed solving for current k=i, program will continue with next i, and print special warning in results file*/
						running_flag = 1;
						scores[i-lower_bound] = -1;
						continue;
					}
				}else{ /* i==upper bound, CPLEX has failed to solve problem for the upper bound.*/
					printf("Warning: failed to solve problem for the upper bound.\nbest_clusters.xgmml file was created for a lower k.\n");
					break;
				}
			}else if(running_flag == -1){/* failure of a standard function, must exit program */
				/* Memory is freed at the end of this function */
				break;
			}

			/* Save objective function solution value */
			if(running_flag == 1)
				scores[i-lower_bound] = *objval;

			/* Create best results file */
			if((i == true_upper_bound)&&(running_flag == 1)) /* Program has reached the "true" upper bound and will produce results and best_scores files now */
				running_flag = create_results_file(scores, lower_bound, upper_bound); /* create_results_file prints warnings if needed */

			/* Create xgmml file*/

			running_flag = Create_ClusterSet_Data_Stracture(&currentSet, &tCluster, i );

			if(running_flag == 1)
				CreateXgmml(pXMLDoc, currentSet, output_path,0);

			xmlCleanupParser();
			xmlMemoryDump();

			/* Create best_clusters file*/
			if(running_flag == 1){
				/* Every iteration Creates Best_Clusters, in case biggest k calculation fails */
				if(i<6) /* In this case we have at least 5 clusters - no problem, take 'em all! */
				{
					CreateXgmml(pXMLDoc, currentSet,output_path,1);
				}
				else /* We have more than 5 clusters. Bummer! must work hard! */
				{
					create_five_best_clusters(upper_bound);
				}
			}


			/* Free xgmml related memory after creating file */
			for (j=currentSet->size-1;j>=0;j--)
				free(currentSet->clusterCollection[j].vertexCollection);

			free(currentSet->clusterCollection);
			free(currentSet);
			free(tCluster);

			xmlCleanupParser();
			xmlMemoryDump();

		} /* for loop...*/
	} /* if before for...*/


	/* ===================== */
	/* Stage 4 - free memory */
	/* ===================== */

	/*double_free_and_null(&scores);*/
	free(scores);
	free(output_path);

	/*Release XmlDoc*/
	xmlFreeDoc(pXMLDoc);
	xmlCleanupParser();
	xmlMemoryDump();

	/*Release xgmml related memory */
	free(networkVertexs);
	free(networkEdges);

	/* quit net */
	quit(global_net);

	/* End program */
	return 1;
}



/*********************************************/
/*---- Network functions (EX 2 functions)----*/
/*********************************************/

/* This function adds vertices to the network */
void addVertex(Network* net, char* verName){

	int i;
	char *checked_vertex_name;
	Vertex* newVer;
	Vertex* checked_vertex;
	Vertex** vList;
	
	vList = net->vertices;

	/* Check whether a vertex with the given name already exists */
	for(i=0; i<net->size; i++){
		checked_vertex = vList[i];
		checked_vertex_name = checked_vertex->name;
		if(strcmp(checked_vertex_name, verName)==0){
			printf("Warning: Vertex already exists with the name: %s. Repeated line will be ignored.\n", verName);
			return;
		}
	}


	if(net->size == net->maxSize){
		
		vList = (Vertex**)realloc(vList, 2*net->maxSize*(sizeof(Vertex*)));
		net->vertices = vList;

		if(vList == NULL){
			perror("Warning: standard function realloc has failed. Exiting program...\n");
			running_flag=0;
			return;
		}

		net->maxSize = net->maxSize*2;	
	}

	newVer = createVertex(net->size , verName);

	if(newVer == NULL){
		running_flag=0;
		return;
	}

	vList[net->size] = newVer;

	net->size++;

}


void printDegree(Network* net, int id){

	Vertex* v;
	int deg;
	char str[] = "Warning: ID number is not in the system";

	/* Check id validity */
	if(id>=net->size){
		sec_print(net, str);
		return;
	}

	v = *(net->vertices + id);
	deg = v->degree;

	if(printf("%d\n", deg)<0){
		perror("Warning: standard function printf has failed.\n");
		running_flag=0;
		quit(net);
		return;
	}
}

/* This function adds edges to the network */
void addEdge(Network* net, int firstID, int secondID, double weight){

	Vertex** vertices;
	Edge* newEdge1;
	Edge* newEdge2;
	Vertex* v1;
	Vertex* v2;
	LinkedList* temp1;
	LinkedList* temp2;
	LinkedList* net_list_link = NULL;


	/* Check for existing vertices ids */
	if(net->size <= firstID || net->size <= secondID){
		if(printf("Warning: ID number is not in the system, the edge will be ignored.\n")<0){
			perror("Warning: standard function printf has failed. Exiting program...\n");
			running_flag=0;
			return;
		}
		return;
	}

	/* Don't allow loops */
		if(firstID == secondID){
			if(printf("Warning: edge must be between two different vertices, the edge will be ignored.\n")<0){
				perror("Warning: standard function printf has failed. Exiting program...\n");
				running_flag=0;
				return;
			}
			return;
		}
	
	
	vertices = net->vertices;
	v1 = vertices[firstID];
	v2 = vertices[secondID];

	/*The next if condition checkes that the new edge doesn't exist allready*/
	temp1 = getEdgeLink(v1->edges, firstID, secondID);
	temp2 = getEdgeLink(v2->edges, firstID, secondID);

	if(temp1 != NULL || temp2 != NULL){
		if(printf("Warning: double edge was detected, the edge will be ignored.\n")<0){
			perror("Warning: standard function printf has failed. Exiting program...\n");
			running_flag=0;
			return;
		}
		return;
	}

	/* What's below was once above... */ 


	/* Check whether edge's weight is within allowed boundaries: 0 < e.weight <=1 */
	if(weight <= 0){
		if(printf("Warning: weight parameter must be greater then 0, the edge will be ignored.\n")<0){
			perror("Warning: standard function printf has failed. Exiting program...\n");
			running_flag=0;
			return;
		}
		return;
	}


	if(weight > 1){
		if(printf("Warning: weight parameter must be less or equals 1, the edge will be ignored.\n")<0){
			perror("Warning: standard function printf has failed. Exiting program...\n");
			running_flag=0;
			return;
		}
		return;
	}
	

	newEdge1 = createEdge(firstID, secondID, weight);
	newEdge2 = createEdge(firstID, secondID, weight);

	if(newEdge1 == NULL || newEdge2 == NULL){
		running_flag=0;
		return;
	}

	v1->edges = HeadInsert (v1->edges, newEdge1);
	if(v1->edges == NULL){
		running_flag=0;
		return;
	}
	v1->degree++;

	v2->edges = HeadInsert(v2->edges, newEdge2);
	if(v2->edges == NULL){
		running_flag=0;
		return;
	}
	v2->degree++;

	net->weightsSum = net->weightsSum + weight;

	net->totalEdgeNumber = net->totalEdgeNumber + 1;
	newEdge1->edge_id=net->totalEdgeNumber;
	newEdge2->edge_id=net->totalEdgeNumber;
	
	
	net_list_link = (LinkedList *)malloc(sizeof(LinkedList));
	if(net_list_link == NULL){
		perror("Warning: standard function malloc has failed. Exiting program...\n");
		running_flag=0;
		return;
	}

	if(net->edge_list_head!=NULL){
		net->edge_list_current->next = net_list_link;
		net_list_link->prev = net->edge_list_current;
	}else{
		net->edge_list_head = net_list_link;
		net_list_link->prev = NULL;
	}
	
	net_list_link->next = NULL;
	net_list_link->e = newEdge1;
	net->edge_list_current = net_list_link;
}


void idByName(Network* net, char* name){

	Vertex** verticesList;
	Vertex* currVertex;
	int numOfVertex = net->size;
	int index, 	found_vertex=0;
	char* currName;

	verticesList = net->vertices;
	
	for (index = 0; index < numOfVertex; index++){

		currVertex = *(verticesList + index);
		currName = currVertex->name;
	

		if (strcmp(name, currName) == 0){
			found_vertex=1;
			if(printf("%d\n", currVertex->id)<0){
				perror("Warning: standard function printf has failed.\n");
				running_flag=0;
				quit(net);
				return;
			}
		}
	}

	if(found_vertex==0)
		sec_print(net, "idByNameWarning: vertex name is not in the system.");
}


void print(Network* net){

	Vertex** vertices;
	Vertex* currV;
	Vertex* v2;
	LinkedList* listEdges;
	Edge* currE;
	int flag = 0;
	int index;

	if(net->size == 0){
		if(printf("\n")<0){
			perror("Warning: standard function printf has failed\n");
			running_flag=0;
			quit(net);
			return;
		}
		return;
	}

	vertices = net->vertices;

	if(printf("%d vertices:\n", net->size)<0){
		perror("Warning: standard function printf has failed\n");
		running_flag=0;
		quit(net);
		return;
	}

	for(index = 0; index<net->size; index++){

		currV = vertices[index];
		if(printf("%d: %s\n", currV->id + 1, currV->name)<0){  /*G: made a little change here */
			perror("Warning: standard function printf has failed\n");
			running_flag=0;
			quit(net);
			return;
		}

		if(flag == 0){
			listEdges = currV->edges;
			if(listEdges != NULL)
				flag = 1;
		}
	}

	if(flag == 0){
		return;
	}

	if(printf("edges:\n")<0){
		perror("Warning: standard function printf has failed\n");
		running_flag=0;
		quit(net);
		return;
	}
	for(index = 0; index < net->size; index++){

		currV = vertices[index];
		listEdges = currV->edges;

		while(listEdges != NULL){

			currE = listEdges->e;
			if(currE == NULL){
				continue;
			}
			if(currV->id == currE->id1){
				v2 = vertices[currE->id2];
			}
			else{
				v2 = vertices[currE->id1];
			}
			if(currV->id < v2->id){
				
				/*if(printf("%s %s %lf\n", currV->name, v2->name, currE->weight)<0){ I've made a change here  ! ! ! ! */
				if(printf("%s %s %f\n", v2->name, currV->name, currE->weight)<0){
					perror("Warning: standard function printf has failed\n");
					running_flag=0;
					quit(net);
					return;
				}
			}

			listEdges = listEdges->next;
		}
	}

}


void quit(Network* net){

	Vertex** vertices;
	Vertex* currV;
	LinkedList* edges;
	LinkedList* tempLink;
	LinkedList *tempLinkForIteratorNext, *tempLinkForIteratorCurrent;
	int index, num_of_edges;

	num_of_edges = net->totalEdgeNumber;

	if(net->size == 0){
		free(net->vertices);
		free(net);
		return;
	}
	vertices = net->vertices;

	for(index = 0; index < net->size; index++){

		currV = vertices[index];
		edges = currV->edges;

		while (edges != NULL){
			tempLink = edges;
			edges = edges->next;
			free(tempLink->e);
			free(tempLink);
		}
	}

	/* Free edges_iterator's edges list */
	if(num_of_edges != 0){
		tempLinkForIteratorCurrent = global_net->edge_list_head;
		tempLinkForIteratorNext = tempLinkForIteratorCurrent->next;
		for(index=0; index<global_net->totalEdgeNumber; index++){
			free(tempLinkForIteratorCurrent);
			if(tempLinkForIteratorNext == NULL) break;
			tempLinkForIteratorCurrent = tempLinkForIteratorNext;
			tempLinkForIteratorNext = tempLinkForIteratorNext->next;
		}
	}

	/* Free each and every Vertex */
	for(index=0; index<global_net->size; index++){
		free(vertices[index]->name);
		free(vertices[index]);
	}

	free(vertices);
	free(net);

}



/***********************************/
/* Parsing & input check utilities */ 
/***********************************/

/*---- Parsing mechanism ----*/

/* Returns number of parameters in input
	- index - enter input's 1st param (i.e - command type) length
	- skips whitespaces
	- ingnores 1st parameter according to index argument
*/
int number_of_parameters(char *input){
	int i = 0;
	int j = 0;
	
	while(i<501){

		if((input[i]!='\t')&&(input[i]!=' ')&&(input[i]!='-')){ /* check first characther*/
			if((input[i]=='\0') || (input[i]=='\n')) break;
			j++;
		}
		while((input[i]!='\t')&&(input[i]!='-')&&(input[i]!=' ')&&(i<501)){
			if(input[i] == '\n') break;
			i++;
		}
		while(((input[i]=='\t') || (input[i]==' ') || (input[i]=='-'))){
			if(i>500) break;
			i++;
		}
		if((input[i]=='\0') || (input[i]=='\n')) break;
	}
	return j;	
}


/* Returns the 1st parameter/argument following the given index */ 
char* get_next_parameter(char *input, char* parameter){
	int i=0, j=0;
	char tmp[30];
	
	while(isspace(input[i])!=0){ 
		i++; /* Get rid of annoying whitespaces prior to the first param */
	}

	while((isspace(input[i])==0)){  
		if(input[i]=='-'){
			input[i]=' ';
			break; /* '-' separates the node names of an edge */
		}
		tmp[j]=input[i];
		if(input[i]=='\0') break;
		input[i]=' ';
		j++;
		i++;
	}
	
	tmp[j]='\0'; 
	strcpy(parameter, tmp);
	return parameter;
}


/* Returns the number of whitespaces at the beggining of each line (before command/1st argument) */
int whitespace_shift(char *input){
	int i=0;
	
	while(i<30){
		if(isspace(input[i])){
			i++;
		}else{
			break;
		}
	}

	return i;

}


/*---- Input check ----*/

/* checking whether or not input has only whitespaces, or is empty (E.C #1) */
int check_only_whitespace(Network* net, char *input){
	int i=0;
	int len = strlen(input);

	while(i<len){
		if(isspace(input[i])==0)
			return 1;
		i++;
	}
	sec_print(net, "Warning: command must have at least one character");
	return 0;
}


/* Checking number's validity and converting string to int (S.P #2), if fails returns -1 */
int char_to_int(Network* net, char* number, int op){
	int i=0, int_form=-1;

	if(number == NULL){
		sec_print(net, "Warning: no argumnet was given to char_to_int!");
		return -1;
	}

	/* check if string is made of only number digits. if not - returns -1 */
	while(number[i]!='\0'){
		if(number[i]>='0'&&number[i]<='9'){
			i++;
		}else{
			if(op==6){
				sec_print(net, "Warning: number of clusters parameter must be an integer type bigger than 1"); /* We don't need it anymore, but I'll leave it here */
				return -1;
			}
			sec_print(net, "Warning: number is not a non-negative integer"); /*IS IT?! I don't think that sholud be thrown in this case. CHECK AGAIN*/
			return -1;
		}
	}

	int_form=atoi(number);
	if(int_form>2147483647||((!str_vs_int(number, int_form)))){
		sec_print(net, "Warning: number is not a non-negative integer");
		int_form = -1;
	}
	return int_form;
}


/* Checking number's validity and converting string to int (S.P #3), if fails returns -1 */
double char_to_double(Network* net, char* number){
	int i=0;
	double d_form = -1;
	int came_across_a_dot = 0;

	/* check if string's validity. if not valid, return -1 */
	while(number[i]!='\0'){
		if(number[i]<'0'||number[i]>'9'){ 
			if(number[i]=='.'&&came_across_a_dot==0){
				came_across_a_dot=1;
				i++;
				continue;
			}
			sec_print(net, "Warning: weight parameter is not a non-negative double, the edge will be ignored.");
			return -1;
		}
		i++;
	}

	d_form=atof(number);
	if(d_form<0){ /* '-', '+' signs are rejected, WHAT? this is redundant.*/
		sec_print(net, "Warning: weight parameter is not a non-negative double, the edge will be ignored.\n");
		return -1;
	}
	
	return d_form;
}


int sec_print(Network* net, char* str){
	if(printf("%s\n", str)<0){
			perror("Warning: standard function printf has failed.\n");
			running_flag=0;
			quit(net);
			return -1;
		}
	return 1;
}

/* This function checks wheter str and num represents the same number*/
int str_vs_int(char* str, int num){
	int len, i, digit, char_digit;
	
	len = strlen(str);
	for(i=len-1; i>=0; i--){
		digit = (num - (num/10)*10);
		num = num/10;
		char_digit = str[i]-48;
		if(digit!=char_digit)
			return 0;
	}

	return 1;
}


/**********************************************************************/
/******************* Input reader & network builder *******************/
/**********************************************************************/

/* Returns vertex id, by name. Terible code duplication !!! */
int get_id(Network* net, char* name){

	Vertex** verticesList;
	Vertex* currVertex;
	int numOfVertex = net->size;
	int index;
	char* currName;

	verticesList = net->vertices;
	
	for (index = 0; index < numOfVertex; index++){

		currVertex = *(verticesList + index);
		currName = currVertex->name;
	

		if (strcmp(name, currName) == 0)
			return currVertex->id;
	}

		sec_print(net, "get_idWarning: vertex name is not in the system.\n"); /* only for QA*/
		return -1;
}

/* Extracts lower & upper bounds from input */
int extract_bounds_from_argv(char **argv, int *lb, int *ub, int *true_ub){

	int lower_bound, upper_bound, true_upper_bound;
	int num_of_nodes;
	int flag = 1;

	num_of_nodes = global_net->size;

	lower_bound = char_to_int(global_net, argv[3], 1);
	upper_bound = char_to_int(global_net, argv[4], 1);
	if((lower_bound < 0) || (upper_bound < 0) || (lower_bound == -1) || (upper_bound == -1) || (lower_bound > upper_bound) || (upper_bound == 0)){

		flag = 0;
		if(upper_bound == 0)
			sec_print(global_net, "Warning: upper_bound is equal to 0. Illegal bound.");
		if (lower_bound > upper_bound)
			sec_print(global_net, "Warning: lower_bound is bigger than upper_bound.");
		if((lower_bound == -1) || (upper_bound == -1))
			sec_print(global_net, "Warning: lower_bound or upper_bound parameters are not set correctly.");
		if((lower_bound < 0) || (upper_bound < 0))
			sec_print(global_net, "Warning: lower_bound or upper_bound are smaller than 0.");

		sec_print(global_net, "Warning: Exiting program...");

		/*we don't care about sec_print's returned value here, because we exit progrm anyway. we used it only for correct order of work. */

	}else{
		if(lower_bound == 0){
			*lb = 1;
			running_flag = sec_print(global_net, "Warning: lower bound = 0 and is illegal. clustering will start with k=1.");
		}else{
			*lb = lower_bound;
		}

		*ub = upper_bound;

		/* 1. When upper bound > number of nodes, number of nodes is set to be the new upper bound for the production of results and best_clusters files */
		/* if upper_bound > number_of_nodes, set to create last xgmml file, best_results & results files when loop gets to i=number_of_nodes */

		if(upper_bound > num_of_nodes){
			true_upper_bound = num_of_nodes;
		}else{
			true_upper_bound = upper_bound;
		}

		*true_ub = true_upper_bound;
	}

	return flag;
}

/*This function opens the files nodes and edges in input folder and reads the network from them.*/
int handle_user_input(int arg_count, char** argv){
	
	int flag = 1, argument_length, file_size, i;
	char *nodes_path, *edges_path;

	/*The main program gets 4 arguments from the user: 
		i.   Input folder (relative path + slash)
		ii. Output folder (relative path + slash)
		iii. Lower bound for the number of clusters: L
		iv. Upper bound for the number of clusters: U */
	
	/* Input check */
	/*argc - the number of arguments passed into the program
		 *  from the command line, including the name of the program.*/
		/* Check correct number of args (exactly 4) passed by the user */
	if(arg_count!=5){
		if(arg_count<5)	sec_print(global_net, "Warning: too few arguments were passed by the user. Exiting program...");
		if(arg_count>5)	sec_print(global_net, "Warning: too many arguments were passed by the user. Exiting program...");
		return 0;
	}

	for(i=1; i<3; i++){
		argument_length = 0;
		argument_length = strlen(argv[i]);
		if((argv[i])[argument_length-1]!='/'){
			if(i==1) sec_print(global_net, "Warning: input path is illegal and should end with a slash.");
			if(i==2) sec_print(global_net, "Warning: output path is illegal and should end with a slash.");
			flag = 0;
		}
	}
	if(flag == 0){
		sec_print(global_net, "Warning: Exiting program...");
		return 0;
	}


	/* Prepare output path */
	output_path = (char*)malloc(sizeof(char)*strlen(argv[2]) + 1); /* Freed in "main()" func., in the end */
	flag = char_allocation_verifier(output_path);
	strcpy (output_path, argv[2]);
	
	if(flag == 1){
		/* Prepare nodes path */
		nodes_path = (char*)malloc(sizeof(char)*strlen(argv[1]) + 5 +1);
		flag = char_allocation_verifier(nodes_path);
		strcpy (nodes_path, argv[1]);
		strcat (nodes_path,"nodes");
	}

	if(flag == 1){
		/* Prepare edges path */
		edges_path = (char*)malloc(sizeof(char)*strlen(argv[1]) + 5 +1);
		flag = char_allocation_verifier(edges_path);
		strcpy (edges_path, argv[1]);
		strcat (edges_path,"edges");
	}
	
	/* Open files for reading */
	
	if(flag == 1){
		nodes = fopen(nodes_path, "r");
		if(nodes==NULL){
			sec_print(global_net, "Warning: can't open nodes file. please check the given path, and try again. Exiting program...\n");
			flag = 0;
		}else{
			fseek (nodes, 0, SEEK_END);
			file_size = ftell(nodes);
			if(file_size == 0){ /* Check whether file is empty or not */
				sec_print(global_net, "Warning: nodes file is empty and thus illegal. Exiting program...\n");
				flag = 0;
			}
			fseek (nodes, 0, SEEK_SET);
		}
	}

	if(flag == 1){
		edges = fopen(edges_path, "r");
		if(edges==NULL){
			sec_print(global_net, "Warning: can't open edges file. please check the given path, and try again. Exiting program...\n");
			flag = 0;
		}
	}
	
	/* Build net */
	if(flag == 1){
		flag = build_network(global_net);
	}
	
	fclose(nodes);
	fclose(edges);
	char_free_and_null(&nodes_path);
	char_free_and_null(&edges_path);

	return flag;
}

/*This function builds the network out of the input files.*/
int build_network(Network *net){

	char input_data[501], node_name[501], edge_data[501], trash_bin[501]; /*maximum line length is 500...*/
	char *protein_str = "protein:";
	char *interaction_str = "interaction:";
	char *error_detector = NULL;
	int i=0, first_vertex_id = 0, second_vertex_id = 0, param_number, flag = 1;
	double weight = 0;

	/* Add nodes from input */

	while(!feof(nodes)){

		error_detector = fgets(input_data, 501, nodes);
		if ((error_detector == NULL)&&(ferror (nodes) != 0)){
		    printf ("Warning: standard function fgets has failed. Exiting program...\n");
		    return 0;
		}

		param_number = number_of_parameters(input_data);
		if((param_number != 2)&&(param_number != 0)){
			sec_print(global_net, "Warning: nodes file: Illegal number of words in line. Line is illegal and will be ignored.");
			continue;
		}
		if(param_number == 0) continue;/* empty line, we just ignore and continue to the next one */

		/* Check correctness and get rid of "protein:" */
		get_next_parameter(input_data, trash_bin);
		if(strcmp(trash_bin, protein_str) != 0){
			sec_print(global_net, "Warning: 1st word in line is not \"protein:\". Line is illegal and will be ignored.");
			continue;
		}
		get_next_parameter(input_data, node_name);
		if(strlen(node_name)==0) break;
		addVertex(net, node_name);	
		if(running_flag == 0)  /* malloc failed during addVertex run, exit program */
			return 0;
		}

	if(net->size == 0){ /* if no node exists, it's an error causing exit */
		sec_print(global_net, "Warning: Network contains 0 nodes, thus Illegal. Exiting program...");
		return 0;
	}

	/* add edges from input */
			
	while(!feof(edges)){

		error_detector = fgets(input_data, 501, edges);
		if ((error_detector == NULL)&&(ferror (edges) != 0)){
			printf ("Warning: standard function fgets has failed. Exiting program...\n");
			return 0;
		}

		param_number = number_of_parameters(input_data);
		if((param_number != 4)&&(param_number != 0)){
			sec_print(global_net, "Warning: edges file: Illegal number of words in line. Line is illegal and will be ignored.");
			continue;
		}
		if(param_number == 0) continue; /* empty line, we just ignore and continue to the next o */

		/* Check correctness and get rid of "interaction:" */
		get_next_parameter(input_data, trash_bin);
		if(strcmp(trash_bin, interaction_str) != 0){
			sec_print(global_net, "Warning: 1st word in line is not \"interaction:\". Line is illegal and will be ignored.");
			continue;
		}
		/* Fetch the first vertex */
		get_next_parameter(input_data, edge_data);
		first_vertex_id = get_id(net, edge_data);
		if(first_vertex_id==-1){ /*meaning - first_vertex_id is not a valid int number*/
			flag = sec_print(net, "Warning: first vertex given is not a vertex in the net. The edge will be ignored.");
			continue;
		}
		/* Fetch the second vertex */
		get_next_parameter(input_data, edge_data);
		second_vertex_id = get_id(net, edge_data);
		if(second_vertex_id==-1){ /*meaning - second_vertex_id is not a valid int number*/
			flag = sec_print(net, "Warning: second vertex given is not a vertex in the net. The edge will be ignored.");
			continue;
		}
		/* Fetch the weight */
		get_next_parameter(input_data, edge_data);
		weight = char_to_double(net, edge_data); 
		if(weight==-1){ /*meaning - weight is not a valid double number */
			/* in case of a problem, "char_to_double" prints a warning */
			continue;
		}
		
		addEdge(net, first_vertex_id, second_vertex_id, weight);
		if(running_flag == 0) return 0; /* if running_flag == 0, malloc failed during addEdge run, exit program */

		i++;
		if(flag<=0) return 0;
	}

	return 1;

}



/****************************************/
/*   Build memory image & call CPLEX    */
/****************************************/

/* solves k_cluster problem - builds data and sends to CPLEX */
int solve_k_cluster_problem(int number_of_clusters){
	

	int flag;
	int objsen;
	double *obj = NULL;
	char *sense = NULL;
	double *rhs = NULL;
	int *matbeg=NULL;
	int *matcnt= NULL;
	int *matind= NULL;
	double *matval= NULL;
	int cnt;
	int *indices = NULL;
	char *ctype = NULL;
	double *lp = NULL;
	double *up = NULL;
	double *x = NULL;
	char *probname;

	int beg_size, ind_size, number_of_constraints, number_of_variables, num_of_var_instances, edges_num, vertices_num;
	int i, j, instances_counter, cur_var_instances, constraint_id;
	Vertex *cur_vertex= NULL;
	Edge *cur_edge= NULL;
	edge_iterator edge_iter;
	vertex_iterator ver_iter;

	flag = 1;

	/* initialize CPLEX arrays */
	edges_num = global_net->totalEdgeNumber;
	vertices_num = global_net->size;

	number_of_constraints = (3 * number_of_clusters*global_net->totalEdgeNumber) + (vertices_num) + number_of_clusters;  /*Total # of constraints: 3*k*|E| + |V| + k*/
	number_of_variables = number_of_clusters*(edges_num+vertices_num); /* Total number of variables= k*(|E|+|V|) */
	num_of_var_instances = number_of_clusters*(7*edges_num+2*vertices_num); /* Total # of instances: k*(7|E|+2|V|) */

	beg_size = number_of_variables; /* # of variables */
	ind_size = num_of_var_instances; /*# of all variables instances */

	/* Memory allocation + Verification- free when done */
	matbeg = (int*)malloc(sizeof(int)*beg_size); /* where the variable starts. malloc size according to serial id of the variable */
	matcnt = (int*)malloc(sizeof(int)*sizeof(int)*beg_size); /* malloc size according to serial id of the variable */
	matind = (int*)malloc(sizeof(int)*ind_size); /* Constraint's id. Total number of vars instances (NOT INCLUDING 0's) */
	matval = (double*)malloc(sizeof(double)*ind_size); /* value in the constraint. Total number of vars instances (NOT INCLUDING 0's) */
	sense = (char*)malloc(sizeof(char)*number_of_constraints);
	rhs = (double*)malloc(sizeof(double)*number_of_constraints);
	indices = (int*)malloc(sizeof(int)*number_of_variables);
	ctype = (char*)malloc(sizeof(char)*number_of_variables);
	lp = (double*)malloc(sizeof(double)*number_of_variables);
	up = (double*)malloc(sizeof(double)*number_of_variables);
	x = (double*)malloc(sizeof(double)*number_of_variables);
	i = strlen(output_path);
	probname = (char*)malloc(sizeof(char)*(i + number_of_clusters + 3)); /* i for out_path, number_of_clusters for safety, 3 for ".lp" */

	/* Build obj array*/
	objsen = CPX_MAX;
	flag = create_objective_array(number_of_clusters, &obj);
	if(flag == 0)
		obj = NULL; /* next block will handle exit */

	/* Verify allocations - if a problem was discovered - exit */
	if((int_allocation_verifier(matbeg)==0) ||(int_allocation_verifier(matcnt)==0)|| (double_allocation_verifier(matval)==0) ||
			(int_allocation_verifier(matind)==0) ||double_allocation_verifier(rhs)==0||
			(char_allocation_verifier(sense)==0) || (int_allocation_verifier(indices)==0) ||
			(char_allocation_verifier(ctype)==0) || (double_allocation_verifier(lp)==0) ||
			(double_allocation_verifier(up)==0)  ||(double_allocation_verifier(x)==0) ||
			(char_allocation_verifier(probname)==0) || (double_allocation_verifier(obj)==0)){
		flag = -1;
		free_null_constraint_memory(obj, sense, rhs, matbeg, matcnt,matind, matval, indices, ctype, lp, up, x,probname);
		return flag;
	}; 


	/* Fill in matbeg & matcnt */

	/* Edges - Z_a_b */
	instances_counter = 0;
	new_edge_iterator(global_net, &edge_iter);
	for(i=0; i<edges_num*number_of_clusters; i+=number_of_clusters){
		cur_edge = edge_iter.currnet_edge;
		cur_edge->matbeg_id = instances_counter;
		cur_edge->indval_index = cur_edge->matbeg_id;
		for(j=0; j< number_of_clusters; j++){
			matbeg[i+j] = instances_counter; /* i = edge_id -1*/
			matcnt[i+j] = 3;
			instances_counter +=3;
		}
		next_edge_NEW(&edge_iter);
	}
	
	/* Vertices - X_a_b */
	new_vertex_iterator(global_net, &ver_iter);
	for(i=edges_num*number_of_clusters ; i<(edges_num + vertices_num)*number_of_clusters; i+=number_of_clusters){
		cur_vertex = ver_iter.currnet_vertex;
		cur_vertex->indval_index = instances_counter;
		cur_vertex->matbeg_id = instances_counter;
		cur_var_instances = 2*cur_vertex->degree + 2; /*2* vertex.degree+2*/
		cur_vertex->instance_number = cur_var_instances;
		for(j=0; j< number_of_clusters; j++){
			matbeg[i+j] = instances_counter;
			matcnt[i+j] = cur_var_instances;
			instances_counter += cur_var_instances; /* #ofVertexInstances = 2*vertex.degree+2 */
		}
		next_vertex_NEW(&ver_iter);
	}

	/* Fill in arrays with problem's data */

	/* CONSTRAINT TYPE i */
	fill_in_constraint_1(number_of_clusters, &constraint_id, &edge_iter, matind, matval, matbeg, rhs, sense);

	/* CONSTRAINT TYPE ii */
	fill_in_constraint_2(number_of_clusters, &constraint_id, &edge_iter, matind, matval, matbeg, rhs, sense);

	/* CONSTRAINT TYPE iii */
	fill_in_constraint_3(number_of_clusters, &constraint_id, &edge_iter, matind, matval, matbeg, rhs, sense);

	/* CONSTRAINT TYPE iv */
	fill_in_constraint_4(number_of_clusters, &constraint_id, &edge_iter, matind, matval, matbeg, rhs, sense);


	/* Set variables' type (BINARY) */
	cnt = number_of_variables;
	for(i=0; i<number_of_variables; i++){
		indices[i] = i;
		ctype[i] = 'B';
	}

	/* Build lp & up arrays */
	for(i=0; i<number_of_variables; i++){
		lp[i] = 0;
		up[i] = CPX_INFBOUND;
	}

	/* Prepare probname */
    if( sprintf (probname, "%s%d.lp", output_path, number_of_clusters)<0){
	 		perror("Warning: standard function sprintf has failed. Exiting program...\n");
	 		flag = -1;
	 		free_null_constraint_memory(obj, sense, rhs, matbeg, matcnt,matind, matval, indices, ctype, lp, up, x,probname);
	 		return flag; /* Exit program ...*/
	 }

    /* Call k_cluster to solve problem using CPLEX */
	flag = k_cluster(&number_of_variables, &number_of_constraints,
			&objsen, &obj, &rhs,
	        &sense, &matbeg, &matcnt, &matind, &matval, &lp,
	        &up, &cnt, &indices, &ctype, &objval, &x, &probname);

	if(flag == 1){ /* CPLEX calculation succeeded */
		assign_solution_values(x, number_of_variables, number_of_clusters);
	}

	/* Free memory */
	free_null_constraint_memory(obj, sense, rhs, matbeg, matcnt,matind, matval, indices, ctype, lp, up, x,probname);

	/* EXIT LOGIC */
	/* flag == 0 when CPLEX calculation has failed, return solve_k_cluster_problem failure for this k */
	/* flag == 1 when CPLEX calculation succeeded, return solve_k_cluster_problem success */
	/* flag == -1 means standard function error occurred - propagate exit program*/
	return flag;
}

/* builds objective array ...*/
int create_objective_array(int number_of_clusters, double **obj_p){
	int i, j, num_of_edges, num_of_ver;
	double *obj = NULL;
	vertex_iterator ver_iter;
	edge_iterator edge_iter;

	/*objsen = CPX_MAX*/

	/*initialize iterators*/
	new_vertex_iterator(global_net, &ver_iter);
	new_edge_iterator(global_net, &edge_iter);

	num_of_edges = global_net->totalEdgeNumber;
	num_of_ver = global_net->size;

	/* allocate memory for OBJ array */
	obj = (double *)malloc(sizeof(double)*(num_of_edges + num_of_ver)*number_of_clusters);
    if(obj == NULL){
		perror("Warning: standard function malloc has failed. Exiting program...\n");
    	return 0;
	}

	/* build OBJ array */
	/* Inserting weight values for edges variables */
	for(j=0; j<num_of_edges*number_of_clusters; j+=number_of_clusters){
		for(i=0; i<number_of_clusters;i++){
			obj[j+i]=edge_iter.currnet_edge->weight;
		}
		next_edge_NEW(&edge_iter);
	};
	/*
	for(j=0; j<num_of_edges; j++){
		for(i=0; i<number_of_clusters;i++){
			obj[j+i*num_of_edges]=edge_iter.currnet_edge->weight;
		}
		next_edge_NEW(&edge_iter);
	};
	*/



	/* Inserting 0 values for vertices variables */
	for(i=num_of_edges*number_of_clusters; i<(num_of_edges + num_of_ver)*number_of_clusters; i++){
		obj[i] = 0;
	}

	*obj_p = obj;

	return 1;

	/* QA */
	/*printf("\n\nObjective array ordered by clusters:...\n");
	for(i=0; i<number_of_clusters;i++){
		printf("Cluster# %d: ", i+1);
		for(j=0; j<num_of_edges;j++)
			printf("%f, ", obj[i*num_of_edges+j]);
		printf("\n");
	}*/
	/* print OBJ array*/
	/*for(i=0; i<(num_of_edges + num_of_ver)*number_of_clusters; i++){
		printf("%f ",  obj[i]);
	}*/

}

/* This function builds CONSTRAINT TYPE i */
int fill_in_constraint_1(int number_of_clusters, int *constraint_id_p, edge_iterator *edge_iter_p, int *matind, double *matval, int *matbeg, double *rhs, char *sense){

	int edges_num, constraint_id;
	int i, j, cluster_counter;
	int cur_indval_index, cur_shifted_indval_index;
	Edge *cur_edge = NULL;
	Vertex *cur_vertex;
	Vertex **vertices_arr = global_net->vertices;
	edge_iterator edge_iter = *edge_iter_p;

	/*initializations*/
	edges_num = global_net->totalEdgeNumber;
	new_edge_iterator(global_net, &edge_iter);
	constraint_id = 0;

	for(i=0; i<edges_num; i++){ /*1 iteration for every edge and every cluster (specific Z_a_b in each iteration)*/

		cur_edge = edge_iter.currnet_edge;

		for(cluster_counter=1; cluster_counter<=number_of_clusters; cluster_counter++){

			cur_vertex = vertices_arr[cur_edge->id1];

			for(j=1; j<=2; j++){ /* Edge "i", cluster "cluster_counter", vertex "j" */

				sense[constraint_id]='L';
				rhs[constraint_id]=0;

				/* Edge data */
				cur_shifted_indval_index = cur_edge->indval_index + cluster_counter-1;
				matind[cur_shifted_indval_index] = constraint_id; /* Constraint's number */
				matval[cur_shifted_indval_index] = 1; /* Value for edge Z_i_j */

				/* Vertex data */
				cur_indval_index = cur_vertex->indval_index; /*Where current vertex begins. indval_index is initially taken from instances counter whlie initializing matbeg for vertices*/
				cur_shifted_indval_index = cur_indval_index + (cluster_counter - 1)*cur_vertex->instance_number; /* compansation for correct VERTEX_CLUSTER variable*/
				matind[cur_shifted_indval_index] = constraint_id;
				matval[cur_shifted_indval_index] = -1;

				/* index increment */

				if(cluster_counter==number_of_clusters){
					cur_vertex->indval_index++ ;
				}
				cur_edge->indval_index++ ;
				cur_vertex = vertices_arr[cur_edge->id2];
				constraint_id++;
			}

		}

		/*Continue to next Edge */
		cur_edge->indval_index = cur_edge->matbeg_id+2;
		next_edge_NEW(&edge_iter);
		cur_edge = edge_iter.currnet_edge;
	}

	*constraint_id_p = constraint_id;
	return 1;
}

/* This function builds CONSTRAINT TYPE ii */
int fill_in_constraint_2(int number_of_clusters, int *constraint_id_p, edge_iterator *edge_iter_p, int *matind, double *matval, int *matbeg, double *rhs, char *sense){

	int edges_num, constraint_id;
	int i, cluster_counter;
	int cur_indval_index, cur_shifted_indval_index;
	Edge *cur_edge = NULL;
	Vertex *vertex_1, *vertex_2;
	Vertex **vertices_arr = global_net->vertices;
	edge_iterator edge_iter = *edge_iter_p;

	/* Initializations */
	new_edge_iterator(global_net, &edge_iter);
	constraint_id = *constraint_id_p;
	edges_num = global_net->totalEdgeNumber;


	for(i=0; i<edges_num; i++){ /*1 iteration for every edge and every cluster (specific Z_a_b in each iteration)*/

		cur_edge = edge_iter.currnet_edge;
		vertex_1 = vertices_arr[cur_edge->id1];
		vertex_2 = vertices_arr[cur_edge->id2];

		for(cluster_counter=1; cluster_counter<=number_of_clusters; cluster_counter++){

			sense[constraint_id]='G';
			rhs[constraint_id]=-1;

			/* Edge data */
			cur_shifted_indval_index = cur_edge->indval_index + 3*(cluster_counter-1);
			matind[cur_shifted_indval_index] = constraint_id; /* Constraint's number */
			matval[cur_shifted_indval_index] = 1; /* Value for edge Z_i_j */

			/* Vertex 1 data */
			cur_indval_index = vertex_1->indval_index; /*Where current vertex begins. indval_index is initially taken from instances counter whlie initializing matbeg for vertices*/
			cur_shifted_indval_index = cur_indval_index + (cluster_counter - 1)*vertex_1->instance_number; /* compansation for correct VERTEX_CLUSTER variable*/
			matind[cur_shifted_indval_index] = constraint_id;
			matval[cur_shifted_indval_index] = -1;

			/* Vertex 2 data */
			cur_indval_index = vertex_2->indval_index; /*Where current vertex begins. indval_index is initially taken from instances counter whlie initializing matbeg for vertices*/
			cur_shifted_indval_index = cur_indval_index + (cluster_counter - 1)*vertex_2->instance_number; /* compansation for correct VERTEX_CLUSTER variable*/
			matind[cur_shifted_indval_index] = constraint_id;
			matval[cur_shifted_indval_index] = -1;

			/* index increment */
			if(cluster_counter==number_of_clusters){
				vertex_1->indval_index++ ;
				vertex_2->indval_index++ ;
			}
			constraint_id++;
		}


		next_edge_NEW(&edge_iter);
	}

	*constraint_id_p = constraint_id;
	return 1;
}

/* This function builds CONSTRAINT TYPE iii */
int fill_in_constraint_3(int number_of_clusters, int *constraint_id_p, edge_iterator *edge_iter_p, int *matind, double *matval, int *matbeg, double *rhs, char *sense){

	int constraint_id, vertices_num;
	int i, cluster_counter;
	int cur_indval_index, cur_shifted_indval_index;
	Vertex *cur_vertex;
	vertex_iterator ver_iter;

	new_vertex_iterator(global_net, &ver_iter);
	constraint_id = *constraint_id_p;
	vertices_num = global_net->size;

	/* Type iii constraints - 5*|E|*k -> k(5|E| + |V|) -1*/
	for(i=0; i<vertices_num; i++){

		cur_vertex=ver_iter.currnet_vertex;

		sense[constraint_id]='E';
		rhs[constraint_id]=1;

		for(cluster_counter=1; cluster_counter<=number_of_clusters; cluster_counter++){
			cur_indval_index = cur_vertex->indval_index; /*Where current vertex begins. indval_index is initially taken from instances counter whlie initializing matbeg for vertices*/
			cur_shifted_indval_index = cur_indval_index + (cluster_counter - 1)*cur_vertex->instance_number; /* compansation for correct VERTEX_CLUSTER variable*/
			matind[cur_shifted_indval_index] = constraint_id;
			matval[cur_shifted_indval_index] = 1;
		}

		constraint_id++;
		cur_vertex->indval_index++;
		next_vertex_NEW(&ver_iter);
	}

	*constraint_id_p = constraint_id;
	return 1;
}

/* This function builds CONSTRAINT TYPE iv */
int fill_in_constraint_4(int number_of_clusters, int *constraint_id_p, edge_iterator *edge_iter_p, int *matind, double *matval, int *matbeg, double *rhs, char *sense){

	int constraint_id, vertices_num;
	int i, cluster_counter;
	int cur_indval_index, cur_shifted_indval_index;
	Vertex *cur_vertex;
	vertex_iterator ver_iter;

	new_vertex_iterator(global_net, &ver_iter);
	constraint_id = *constraint_id_p;
	vertices_num = global_net->size;

	/* Type iv constraints - k(5|E| + |V|) -> k(5|E| + 2|V|)*/

	for(cluster_counter=1; cluster_counter<=number_of_clusters; cluster_counter++){

		new_vertex_iterator(global_net, &ver_iter);
		sense[constraint_id]='G';
		rhs[constraint_id]=1;

		for(i=0; i<vertices_num; i++){
			cur_vertex=ver_iter.currnet_vertex;
			cur_indval_index = cur_vertex->indval_index;
			cur_shifted_indval_index = cur_indval_index + (cluster_counter - 1)*cur_vertex->instance_number; /* compansation for correct VERTEX_CLUSTER variable*/
			matind[cur_shifted_indval_index] = constraint_id;
			matval[cur_shifted_indval_index] = 1;
			next_vertex_NEW(&ver_iter);
		}
		constraint_id++;
	}

	*constraint_id_p = constraint_id;
	return 1;
}



/*****************************/
/*   results file creators   */
/*****************************/

/* This function creates the "results" file */
int create_results_file(double *scores, int lw_bound, int up_bound){

	int i, j, num_of_ver, num_of_edges, flag, cluster_id, cluster_size, num_of_clusters, diameter;
	int *array_of_clusters, *array_of_cluster_sizes;
	double average_values[2] = {0};
	Network *net = global_net;
	edge_iterator edge_iter;
	FILE *result_file = NULL;
	Vertex *vertex_1;
	Vertex *vertex_2;
	Vertex *current_vertex;
	Vertex **vertices = net->vertices;
	Edge *current_edge;
	char *path;

	flag = 1;
	num_of_ver = net->size;
	num_of_edges = net->totalEdgeNumber;
	num_of_clusters = up_bound;

	/*Open file for writing */
	path = (char*)malloc(sizeof(char)*(strlen(output_path)+8));
	flag = char_allocation_verifier(path);
	if(flag == 0){
		sec_print(net, "Warning: there was a problem writing \"result\" file. Exiting the program...\n");
		return flag;
	}else{
		strcpy (path, output_path);
		strcat (path,"results");
	}

	result_file = fopen(path, "w");
	if(result_file == NULL){
		sec_print(net, "Warning: there was a problem writing \"result\" file. Exiting the program...\n");
		flag = 0;
		free(path);
		return flag;
	}

	/* Section 1 - Print scores for all clustering range */
	for(i=lw_bound; i<=up_bound; i++){
		if(flag >0){
			if(i > num_of_ver){ /* meaning that k > number of nodes, which is an infeasible solution. */
				flag = fprintf(result_file, "Clustering with k=%d: infeasible\n", i);
			}else{ /* when boundaries are ok - */
				if(scores[i - lw_bound] == -1){ /* There was an error in CPLEX run for this k */
					flag = fprintf(result_file, "Clustering with k=%d: %s", i, "There was a problem with CPLEX run with this particular k.\n");
				}else{ /* When everything is OK, print expected line */
					flag = fprintf(result_file, "Clustering with k=%d: %.3f\n", i, scores[i - lw_bound]);
				}
			}
		}
	}

	/* if upper bound > number of nodes, no feasible solution is available and program doesn't print sections 2 and 3*/
	if(lw_bound > num_of_ver){
		fclose(result_file);
		free(path);
		return flag;
	}

	/* Section 2 - Print network solution */
	/* if upper bound > number of nodes, we'll define a new upper bound as the biggest upper bound possible - number of nodes */
	if( up_bound > num_of_ver ){
		up_bound = num_of_ver;
	}

	if(flag >0) flag = fprintf(result_file, "\n");
	if(flag >0) flag = fprintf(result_file, "The clustered network for k=%d:\n", up_bound);
	if(flag >0) flag = fprintf(result_file, "%d vertices:\n", num_of_ver);

	/* Print vertices - clusterID */
	if(flag >0){
		for(i=0; i<num_of_ver; i++){
			current_vertex = vertices[i];
			if(flag >0) flag = fprintf(result_file, "%d: %s %d\n", (i+1), current_vertex->name, current_vertex->clusterID);
		}
	}

	/* Print edges data */
	if((flag >0) && (net->totalEdgeNumber > 0)){
		new_edge_iterator(net, &edge_iter);
		flag = fprintf(result_file, "%d edges:\n", num_of_edges);
		for(i=0; i<num_of_edges; i++){
			current_edge = edge_iter.currnet_edge;

			vertex_1 = vertices[current_edge->id1];
			vertex_2 = vertices[current_edge->id2];

			if(flag >0) fprintf(result_file, "%d: %s-%s %.3f\n", (i+1), vertex_1->name, vertex_2->name, current_edge->weight);
			next_edge_NEW(&edge_iter);
		}
	}

	/* Section 3 - Print statistics for the upper bound option */
	if(flag >0){
		average_weight_calculator(average_values);
	}

	/* Clustering statistics */
	if(flag >0){
		flag = 1;
		array_of_clusters = (int*)malloc(sizeof(int)*num_of_clusters);
		array_of_cluster_sizes = (int*)malloc(sizeof(int)*num_of_clusters);
		if((int_allocation_verifier(array_of_clusters)==0) || (int_allocation_verifier(array_of_cluster_sizes)==0)){
			flag = 0;
		}

		if(flag == 1){
			prepare_cluster_arrays(array_of_clusters, array_of_cluster_sizes, num_of_clusters);
			flag = sort_clusters_by_size(array_of_clusters, array_of_cluster_sizes, num_of_clusters);
		}

		if(flag == 1){
			j=0;
			flag = fprintf(result_file, "\nClustering statistics for %d:\n", up_bound);
			if(flag > 0) flag = fprintf(result_file, "Average weight of an edge within clusters: %.3f\n", average_values[0]);
			if(flag > 0) flag = fprintf(result_file, "Average weight of an edge between clusters: %.3f\n", average_values[1]);
			if(flag > 0){
				for(i=up_bound-1; i>=0; i--){
					j++;
					cluster_id = array_of_clusters[i]; /* Get the correct cluster*/
					cluster_size = array_of_cluster_sizes[i]; /* Get it's size */
					diameter = find_diameter(cluster_id, cluster_size);
					if(diameter == -666){ /* malloc failed, propagate exit */
						flag = 0;
						break;
					}

					if(flag != 0){
						if(diameter > -1){
							flag = fprintf(result_file, "Cluster %d: score-%.3f diameter-%d\n", j, cluster_score(cluster_id), diameter);
						}else{ /* diameter is inf, we follow instructions to print "inf", as mentioned in FAQ document */
							flag = fprintf(result_file, "Cluster %d: score-%.3f diameter-%s\n", j, cluster_score(cluster_id), "inf");
						}
					}

					if(flag <= 0) break; /*meaning, there was an error with fprintf function */
				}
			}
		}
	}

	/* close file & free memory*/
	fclose(result_file);
	free(path);
	int_free_and_null(&array_of_clusters);
	int_free_and_null(&array_of_cluster_sizes);

	/* Exit logic */
	if(flag <= 0){
		sec_print(net, "Warning: there was a problem writing \"result\" file. Exiting the program...\n");
		return 0;
	}

	return 1;
}

/* This functions takes CPLEX solution and assign clusterID values to edges and nodes */
void assign_solution_values(double *solution_array, int num_of_variables, int num_of_clusters ){

	int i, cluster_number, index, edge_not_included;
	double solution_value;
	int number_of_edges = global_net->totalEdgeNumber, number_of_nodes = global_net->size;
	edge_iterator edge_iter;
	Edge *cur_edge;
	Vertex **vertices = global_net->vertices;


	/* Assign values for EDGES */
	new_edge_iterator(global_net, &edge_iter);

	for(i=0; i<number_of_edges; i++){
		cur_edge = edge_iter.currnet_edge;
		edge_not_included = 0;

		for(cluster_number=0; cluster_number<num_of_clusters; cluster_number++){
			index = i*num_of_clusters + cluster_number;
			solution_value = solution_array[index];
			if(1 - solution_value < 0.00001){
				edge_not_included = 1;
				cur_edge->clusterID = cluster_number + 1;
			}
			if(edge_not_included==0) cur_edge->clusterID = -1;
		}
		next_edge_NEW(&edge_iter);
	}

	/* Assign values for NODES */
	for(i=0; i<number_of_nodes; i++){
		for(cluster_number=0; cluster_number<num_of_clusters; cluster_number++){
			index = i*num_of_clusters + cluster_number + number_of_edges*num_of_clusters;
				solution_value = solution_array[index];
				if(1 - solution_value < 0.00001){
					vertices[i]->clusterID = cluster_number + 1;
				}
		}
	}
}

/* This function calculates the average weight within/between clusters */
void average_weight_calculator(double *average_values){

	int edge_cluster_ID, edges_within, edges_between;
	double cur_weight, weight_within, weight_between;
	double average_within, average_between;
	edge_iterator edge_iter;
	Edge *cur_edge;

	weight_within = 0;
	weight_between = 0;
	edges_within = 0;
	edges_between = 0;

	if(global_net->totalEdgeNumber == 0){
		average_values[0] = 0;
		average_values[1] = 0;
		return;
	}

	new_edge_iterator(global_net, &edge_iter);

	while(hasNextEdge_NEW(edge_iter)){
		cur_edge = edge_iter.currnet_edge;
		cur_weight = cur_edge->weight;
		edge_cluster_ID = cur_edge->clusterID;
		if(edge_cluster_ID != -1){
			edges_within++;
			weight_within += cur_weight;
		}else{
			edges_between++;
			weight_between += cur_weight;
		}
		next_edge_NEW(&edge_iter);
	}

	/* Dealing with the last edge */
	cur_edge = edge_iter.currnet_edge;
	cur_weight = cur_edge->weight;
	edge_cluster_ID = cur_edge->clusterID;
	if(edge_cluster_ID != -1){
		edges_within++;
		weight_within += cur_weight;
	}else{
		edges_between++;
		weight_between += cur_weight;
	}

	/* Calculate averages*/
	if(edges_within > 0){
		average_within = weight_within/edges_within;
	}else{
		average_within = 0;
	}

	if(edges_between > 0){
		average_between = weight_between/edges_between;
	}else{
		average_between = 0;
	}

	average_values[0] = average_within;
	average_values[1] = average_between;

}

/* This function calculates cluster score given the cluster's number/ID*/
double cluster_score(int cluster_ID){

	int edge_cluster_ID;
	double score, cur_weight;
	edge_iterator edge_iter;
	Edge *cur_edge;

	score = 0;

	if(global_net->totalEdgeNumber == 0){
		return score;
	}

	new_edge_iterator(global_net, &edge_iter);
	while(hasNextEdge_NEW(edge_iter)){
		cur_edge = edge_iter.currnet_edge;
		cur_weight = cur_edge->weight;
		edge_cluster_ID = cur_edge->clusterID;
		if(cluster_ID == edge_cluster_ID) score += cur_weight;
		next_edge_NEW(&edge_iter);
	}

	/* Dealing with the last edge */
	cur_edge = edge_iter.currnet_edge;
	cur_weight = cur_edge->weight;
	edge_cluster_ID = cur_edge->clusterID;
	if(cluster_ID == edge_cluster_ID) score += cur_weight;

	return score;
}


/* This function returns the given cluster's diameter OR -1 for representing infinity */
/* diameter = max{lsp_via_BFS(v): for all v in the cluster} OR -1 (when there are more than one unaccessible vertices in the cluster)*/
int find_diameter(int cluster_id, int cluster_size){

	int diameter, current_lsp, isolated_vertex_exists;
	int i, net_size;
	Vertex **vertices = global_net->vertices;
	Vertex *cur_vertex;

	diameter = 0;
	isolated_vertex_exists = 0;
	current_lsp = 0;
	net_size = global_net->size;


	for(i=0; i<net_size; i++){
		cur_vertex = vertices[i];
		if(cur_vertex->clusterID == cluster_id){
			current_lsp = lsp_via_BFS(cur_vertex, cluster_size);
			if(current_lsp == -1){
				return -666;
			}
			if(diameter < current_lsp) diameter = current_lsp;
			if(current_lsp == 0) isolated_vertex_exists++ ; /* if lsp is 0, then this vertex doesn't have any "neighbors" in this clusters */
			if(isolated_vertex_exists > 1) /* there is more then one isolated vertex in this cluster, and thus diameter = inf */
			{
				diameter = -1;
				break;
			}
		}
	}

	return diameter;

}

/* Calculates and returns longest shortest path from "source" within the given cluster, using BFS algorithm */
int lsp_via_BFS(Vertex *source, int cluster_size){

	int *dist = NULL;
	int i, net_size, infinity, cluster_id, front, longest_shortest_path;
	Vertex **vertices;
	Vertex *cur_vertex, *adj_vertex;
	Queue_fifo *queue;
	Edge *cur_edge;
	LinkedList *cur_edge_link;

	infinity = -12;
	net_size = global_net->size;
	longest_shortest_path = 0;
	queue = createQueue(net_size);

	cur_vertex = source;
	cluster_id = cur_vertex->clusterID;
	vertices = global_net->vertices;

	/* allocate & initizlize dist */
	dist = (int*)malloc(sizeof(int)*net_size);
	if(dist == NULL){
		perror("Warning: standard function malloc has failed. Exiting program...\n");
		return -1;
	}

	for(i=0; i<net_size; i++){
		dist[i] = infinity;
	}

	/*dist[f] = 0 */
	dist[source->id] = 0;

	/* initialize queue */
	Enqueue(queue, source->id);

	while (is_queue_empty(queue) == 0)  /* Consider >0*/
	{
		/* u = Q.pop() */
		front = get_front(queue);
		cur_vertex = vertices[front];
		Dequeue(queue);
		if(cur_vertex->degree == 0){
			continue;
		}
		cur_edge_link = cur_vertex->edges;

		front = get_front(queue);

		if(cur_edge_link != NULL){ /* all vertices->edges are initialized to NULL */

			/* for each edge going from u: */
			for(i=1; i<=cur_vertex->degree; i++){
				cur_edge = cur_edge_link->e;
				if(cur_vertex->id == cur_edge->id1){
					adj_vertex = vertices[cur_edge->id2];
				}else{
					adj_vertex = vertices[cur_edge->id1];
				}

				/* if dist[v] = inf */
				if((dist[adj_vertex->id]==infinity)&&(adj_vertex->clusterID==cluster_id)){
					dist[adj_vertex->id] = dist[cur_vertex->id] + 1; /* dist[v] = dist[u] + 1 */
					Enqueue(queue, adj_vertex->id);/* Q.push(v) */
				}
				/* continue with next edge */
				cur_edge_link = cur_edge_link->next;
			}
		}

	}

	/* find the max value (not including infinity) in dist[] */
	for(i=0; i<net_size; i++){

		if((dist[i]!=infinity)&&(longest_shortest_path < dist[i])){
			longest_shortest_path = dist[i];
		}
	}

	/* free memory */
	delete_queue(queue);
	int_free_and_null(&dist);

	return longest_shortest_path;
}



/* Cluster handlers & sort functions*/

void prepare_cluster_arrays(int *array_of_clusters, int *array_of_cluster_sizes, int num_of_clusters){

	int i;

	/* Prepare array_of_clusters*/
	for(i=0; i<num_of_clusters; i++){
		array_of_clusters[i] = i+1;
	}

	/* Prepare array_of_cluster_sizes*/
	cluster_sizes(array_of_cluster_sizes, num_of_clusters);
}

/* This function fills in the size of each cluster in "array_of_cluster_sizes"*/
void cluster_sizes(int *array_of_cluster_sizes, int num_of_clusters){

	int i, cluster_id, num_of_nodes;
	Vertex **vertices;

	num_of_nodes = global_net->size;
	vertices = global_net->vertices;

	/* Initialize array */
	for(i=0; i<num_of_clusters; i++){
		array_of_cluster_sizes[i] = 0;
	}

	for(i=0; i<num_of_nodes; i++){
		cluster_id = vertices[i]->clusterID;
		array_of_cluster_sizes[cluster_id - 1]++;
	}

}

/* Clusters sorting and ordering */
/* @pre: array_of_clusters, array_of_cluster_sizes are already allocated and values-ready*/
/* @post: array_of_clusters holds sorted cluster_ids min-to-max ordering */
int sort_clusters_by_size(int *array_of_clusters, int *array_of_cluster_sizes, int num_of_clusters){

	int i, j, size, clusterID;
	int *temp_array;
	int flag = 1;

	temp_array = (int*)malloc(sizeof(int)*num_of_clusters);
	flag = int_allocation_verifier(temp_array);
	if(flag == 0) return 0;

	for(i=0; i<num_of_clusters; i++){
		temp_array[i] = array_of_cluster_sizes[i];
	}

	qsort (temp_array, num_of_clusters, sizeof(int), compare);

	for(i=num_of_clusters-1; i>=0; i--){
		size = temp_array[i];
		for(j=0; j<num_of_clusters; j++){
			if(array_of_cluster_sizes[j] == size){
				clusterID = j+1;
				array_of_cluster_sizes[j] = -1;
				break;
			}
		}
		array_of_clusters[i] = clusterID;
	}

	cluster_sizes(array_of_cluster_sizes, num_of_clusters);

	for(i=0; i<num_of_clusters; i++){
		array_of_cluster_sizes[i] = temp_array[i];
	}

	/* free memory */
	int_free_and_null(&temp_array);


	return flag;
}

int compare(const void * a, const void * b)
{
  return ( *(int*)a - *(int*)b );
}

/*************************/
/* local XGMML functions */
/*************************/

int create_five_best_clusters(int num_of_clusters)
{
	xmlDocPtr pXMLDoc;

	ClusterSet* currentSet;
	Cluster* tCluster;

	Edge* networkEdges;
	int networkEdgesSize;
	edge_iterator edge_iter;
	int topFiveVertexCount;

	Vertex* networkVertexs;
	int networkVertexsSize;

	int x;
	int maxClusterId;
	double maxWeight;
	int maxArrayIndex;
	int* topFiveClusterIds;

	int j;
	int i;
	int k;
	int relevantSize;
	ClusterSize* clusterSizes;

	int positionFiveSize;

	int flag = 1;
	int *array_of_clusters, *array_of_cluster_sizes;

	array_of_clusters = (int*)malloc(sizeof(int)*num_of_clusters);
	array_of_cluster_sizes = (int*)malloc(sizeof(int)*num_of_clusters);

	if((int_allocation_verifier(array_of_clusters)==0) || (int_allocation_verifier(array_of_cluster_sizes)==0)){
		flag = 0;
		int_free_and_null(&array_of_clusters);
		int_free_and_null(&array_of_cluster_sizes);
	}

	prepare_cluster_arrays(array_of_clusters, array_of_cluster_sizes, num_of_clusters);
	sort_clusters_by_size(array_of_clusters, array_of_cluster_sizes, num_of_clusters);


	/*Assumption num_of_clusters >= 6*/
	relevantSize = 5;
	for (j=num_of_clusters-6;j>=0;j--)
	{
		 if (array_of_cluster_sizes[j]==array_of_cluster_sizes[num_of_clusters-5])
		 {
			 relevantSize++;
		 }
		 else
		 {
			 break;
		 }
	}


	clusterSizes = (ClusterSize*)malloc(sizeof(ClusterSize)*relevantSize);


	x=0;
	for (j=num_of_clusters-1;j>=num_of_clusters-relevantSize;j--)
	{
		clusterSizes[x].size = array_of_cluster_sizes[j];
		clusterSizes[x].clusterId = array_of_clusters[j];
		clusterSizes[x].sumOfWeights = GetClusterSumOfWeights(array_of_clusters[j]);
		clusterSizes[x].touchedForMaxWeight = 0;
		x++;
	}

	/*QA*/
	/*
	for (x=0;x<relevantSize;x++)
		printf("[Id=%d,Size=%d,SumOfWeights=%f]\n",clusterSizes[x].clusterId,clusterSizes[x].size,clusterSizes[x].sumOfWeights);
	*/

	topFiveClusterIds = (int*)malloc(sizeof(int)*5);
	topFiveVertexCount = 0;
	 positionFiveSize = clusterSizes[4].size;
	 for (x=0;x<5;x++)
	 {
		 if (clusterSizes[x].size==positionFiveSize)
		 {
			 break;
		 }
		 else
		 {
			topFiveClusterIds[x] =clusterSizes[x].clusterId;
			topFiveVertexCount += clusterSizes[x].size;
		 }
	 }

	 /*topFiveClusterIds now contains x Items*/
	 /*now we need to complete topFiveClusterIds to 5 items => by taking (5-x) max items from clusterSizes starting at x including*/
	 k=x;
	 for (i=0;i<(5-k);i++)
	 {
		 maxClusterId=0;
		 maxWeight=0;
		 for (j=x;j<relevantSize;j++)
		 {
			 if (clusterSizes[j].sumOfWeights > maxWeight)
			 {
				 if (clusterSizes[j].touchedForMaxWeight == 0)
				 {
					 maxClusterId = clusterSizes[j].clusterId;
					 maxWeight = clusterSizes[j].sumOfWeights;
					 maxArrayIndex = j;
				 }
			 }
		 }

		 topFiveClusterIds[x+i] = maxClusterId;
		 topFiveVertexCount += clusterSizes[maxArrayIndex].size;
		 clusterSizes[maxArrayIndex].touchedForMaxWeight = 1;
	 }


	/*Create networkEdges And networkEdgesSize From Network*/
	networkEdgesSize = 0;
	for (j=0;j<5;j++)
		networkEdgesSize += GetClusterEdgesCount(topFiveClusterIds[j]);

	/*Handle Edges WIth ClusterId == -1*/
	networkEdgesSize += GetCrossClustersEdgesCount(topFiveClusterIds,5);

	networkEdges = (Edge*)malloc(networkEdgesSize*sizeof(Edge));

	x=0;
	new_edge_iterator(global_net, &edge_iter);
	while(hasNextEdge_NEW(edge_iter))
	{
		if (IsContainsValue(edge_iter.currnet_edge->clusterID, topFiveClusterIds,5)==1)
		{
			networkEdges[x].id1 = edge_iter.currnet_edge->id1+1; /*Hack*/
			networkEdges[x].id2 = edge_iter.currnet_edge->id2+1; /*Hack*/
			networkEdges[x].weight = edge_iter.currnet_edge->weight;
			x++;
		}
		next_edge_NEW(&edge_iter);
	}

	if (IsContainsValue(edge_iter.currnet_edge->clusterID, topFiveClusterIds,5)==1)
	{
		networkEdges[x].id1 = edge_iter.currnet_edge->id1+1; /*Hack*/
		networkEdges[x].id2 = edge_iter.currnet_edge->id2+1; /*Hack*/
		networkEdges[x].weight = edge_iter.currnet_edge->weight;
	}

	/*Add Cross Cluster Edges*/
	new_edge_iterator(global_net, &edge_iter);
	while(hasNextEdge_NEW(edge_iter))
	{
		if (edge_iter.currnet_edge->clusterID == -1)
		{
			if (IsContainsValue(global_net->vertices[edge_iter.currnet_edge->id1]->clusterID,topFiveClusterIds,5)==1 && IsContainsValue(global_net->vertices[edge_iter.currnet_edge->id2]->clusterID,topFiveClusterIds,5)==1)
			{
				networkEdges[x].id1 = edge_iter.currnet_edge->id1+1; /*Hack*/
				networkEdges[x].id2 = edge_iter.currnet_edge->id2+1; /*Hack*/
				networkEdges[x].weight = edge_iter.currnet_edge->weight;
				x++;
			}
		}
		next_edge_NEW(&edge_iter);
	}


	if (edge_iter.currnet_edge->clusterID == -1)
	{
			if (IsContainsValue(global_net->vertices[edge_iter.currnet_edge->id1]->clusterID,topFiveClusterIds,5)==1 && IsContainsValue(global_net->vertices[edge_iter.currnet_edge->id2]->clusterID,topFiveClusterIds,5)==1)
			{
				networkEdges[x].id1 = edge_iter.currnet_edge->id1+1; /*Hack*/
				networkEdges[x].id2 = edge_iter.currnet_edge->id2+1; /*Hack*/
				networkEdges[x].weight = edge_iter.currnet_edge->weight;
			}
	}


	/*Create networkVertexs And networkVertexsSize From Network*/
	networkVertexsSize = topFiveVertexCount;
	networkVertexs = (Vertex*)malloc(networkVertexsSize*sizeof(Vertex));
	x=0;
	for (j=0;j<global_net->size;j++)
	{
		if (IsContainsValue(global_net->vertices[j]->clusterID, topFiveClusterIds,5)==1)
		{
			networkVertexs[x].id = global_net->vertices[j]->id+1;/*Hack*/
			networkVertexs[x].name = global_net->vertices[j]->name;
			x++;
		}
	}


	/*Call Create XMLDoc for 5_best_clusters */
	pXMLDoc = CreateXmlDocForNetwork(networkEdges,networkEdgesSize,networkVertexs,networkVertexsSize);

	/*initalize CurrentSet*/
	currentSet = (ClusterSet*)malloc(sizeof(ClusterSet));
	currentSet->size = 5;
	currentSet->clusterCollection = (Cluster*)malloc(5*sizeof(Cluster));

	for (j=0;j<5;j++)
	{
		tCluster = GetClusterById(topFiveClusterIds[j], num_of_clusters);
		currentSet->clusterCollection[j].size = tCluster->size;
		currentSet->clusterCollection[j].vertexCollection = tCluster->vertexCollection;

		free(tCluster);
	}

	/*Create XGMML In Best Mode*/
	CreateXgmml(pXMLDoc, currentSet,output_path,1);

	/*free xml components*/
	xmlFreeDoc(pXMLDoc);
	xmlCleanupParser();
	xmlMemoryDump();


	/*xmlCleanupParser();
	xmlMemoryDump();
*/
	free(array_of_clusters);
	free(array_of_cluster_sizes);
	free(networkEdges);
	free(networkVertexs);

	for (j=currentSet->size-1;j>=0;j--)
		free(currentSet->clusterCollection[j].vertexCollection);
	free(currentSet->clusterCollection);
	free(currentSet);

	free(clusterSizes);
	free(topFiveClusterIds);

	return flag;
}

int Create_Network_Edges_Data_Stractures(Edge** netEdges, int *NetEdgesSize){

	int j;
	int networkEdgesSize = 0;
	int flag = 1;
	Edge *networkEdges;
	edge_iterator edge_iter;

	if(global_net->totalEdgeNumber > 0)
		{
			networkEdgesSize = global_net->totalEdgeNumber;
			*NetEdgesSize = networkEdgesSize;
			networkEdges = (Edge*)malloc(networkEdgesSize*sizeof(Edge));
			if(networkEdges == NULL){
				flag = 0;
				perror("Warning: standard function malloc has failed. Exiting program...\n");
				return flag;
			}

			j=0;
			new_edge_iterator(global_net, &edge_iter);
			while(hasNextEdge_NEW(edge_iter))
			{
				networkEdges[j].id1 = edge_iter.currnet_edge->id1+1; /*Hack*/
				networkEdges[j].id2 = edge_iter.currnet_edge->id2+1; /*Hack*/
				networkEdges[j].weight = edge_iter.currnet_edge->weight;

				j++;
				next_edge_NEW(&edge_iter);
			}


			networkEdges[j].id1 = edge_iter.currnet_edge->id1+1; /*Hack*/
			networkEdges[j].id2 = edge_iter.currnet_edge->id2+1; /*Hack*/
			networkEdges[j].weight = edge_iter.currnet_edge->weight;
		}

	*netEdges = networkEdges;

	return flag;
}

int CreateNetwork_Vertices_Data_Stractures(Vertex** networkVertices, int *NetVerSize){

	int j;
	int networkVertexsSize = 0;
	Vertex *networkVertexs;
	int flag = 1;

	networkVertexsSize = global_net->size;
	networkVertexs = (Vertex*)malloc(networkVertexsSize*sizeof(Vertex));
	if(networkVertexs == NULL){
		flag = 0;
		perror("Warning: standard function malloc has failed. Exiting program...\n");
		return flag;
	}

	for (j=0;j<global_net->size;j++)
	{
		networkVertexs[j].id = global_net->vertices[j]->id+1;
		networkVertexs[j].name = global_net->vertices[j]->name;
	}

	*NetVerSize = networkVertexsSize;
	*networkVertices = networkVertexs;

	return flag;
}

int Create_ClusterSet_Data_Stracture(ClusterSet** currentSet_p, Cluster** tCluster_p, int i ){

	int j;
	ClusterSet* currentSet = NULL;
	Cluster* tCluster = NULL;


	currentSet = (ClusterSet*)malloc(sizeof(ClusterSet));
	if(currentSet == NULL){
		return 0;
	}

	currentSet->size = i;
	currentSet->clusterCollection = (Cluster*)malloc(i*sizeof(Cluster));
	if(currentSet->clusterCollection == NULL){
		free(currentSet);
		return 0;
	}

	for (j=0;j<i;j++){
		tCluster = GetClusterById(j+1,i);
		currentSet->clusterCollection[j].size = tCluster->size;
		currentSet->clusterCollection[j].vertexCollection = tCluster->vertexCollection;
	}

	*currentSet_p = currentSet;
	*tCluster_p = tCluster;

	return 1;
}

int IsContainsValue(int value, int* numbers, int numbersSize)
{
	int j;
	for (j=0;j<numbersSize;j++)
	{
		if (numbers[j] == value)
			return 1;
	}
	return 0;
}

int GetClusterEdgesCount(int clusterId)
{

	int result;
	edge_iterator edge_iter;

	result=0;
	new_edge_iterator(global_net, &edge_iter);
	while(hasNextEdge_NEW(edge_iter))
	{

		if (edge_iter.currnet_edge->clusterID == clusterId)
		{
			result++;
		}
		next_edge_NEW(&edge_iter);
	}

	if (edge_iter.currnet_edge->clusterID == clusterId)
		result++;


	return result;
}

double GetClusterSumOfWeights(int clusterId)
{
	double result;
	edge_iterator edge_iter;

	new_edge_iterator(global_net, &edge_iter);
	while(hasNextEdge_NEW(edge_iter))
	{
		if (edge_iter.currnet_edge->clusterID == clusterId)
		{
			result += edge_iter.currnet_edge->weight;
		}
		next_edge_NEW(&edge_iter);
	}

	if (edge_iter.currnet_edge->clusterID == clusterId)
		result += edge_iter.currnet_edge->weight;

	return result;
}

Cluster* GetClusterById(int clusterId, int k)
{
	int j;
	int index;

	int resultClusterSize;
	Cluster* resultCluster;

	int* arrayOfClusters;
	int* arrayOfClusterSizes;

	/*Get Result Cluster Size*/
	arrayOfClusterSizes = (int*)malloc(sizeof(int)*k);
	arrayOfClusters = (int*)malloc(sizeof(int)*k);
	prepare_cluster_arrays(arrayOfClusters, arrayOfClusterSizes, k);
	resultClusterSize = arrayOfClusterSizes[clusterId-1];

	resultCluster = (Cluster*)malloc(sizeof(Cluster));
	resultCluster->size = resultClusterSize;
	resultCluster->vertexCollection = (Vertex*)malloc(sizeof(Vertex)*resultClusterSize);

	/*Get All Vertices Belong to this Cluster*/
	index=0;
	for (j=0;j<global_net->size;j++)
	{
		if (global_net->vertices[j]->clusterID == clusterId)
		{
			resultCluster->vertexCollection[index].id = global_net->vertices[j]->id+1;
			resultCluster->vertexCollection[index].name = global_net->vertices[j]->name;
			index++;
		}
	}

	free(arrayOfClusterSizes);
	free(arrayOfClusters);

	return resultCluster;
}

int GetCrossClustersEdgesCount(int* topClusterIds, int size){

	int result;
	edge_iterator edge_iter;
	Vertex** vertexs;

	vertexs = global_net->vertices;

	result=0;
	new_edge_iterator(global_net, &edge_iter);
	while(hasNextEdge_NEW(edge_iter))
	{
			if (edge_iter.currnet_edge->clusterID == -1)
			{
				if (IsContainsValue(vertexs[edge_iter.currnet_edge->id1]->clusterID,topClusterIds,size)==1 && IsContainsValue(vertexs[edge_iter.currnet_edge->id2]->clusterID,topClusterIds,size)==1)
					result++;
			}
			next_edge_NEW(&edge_iter);
	}

	if (edge_iter.currnet_edge->clusterID == -1)
	{
		if (IsContainsValue(vertexs[edge_iter.currnet_edge->id1]->clusterID,topClusterIds,size)==1 && IsContainsValue(vertexs[edge_iter.currnet_edge->id2]->clusterID,topClusterIds,size)==1)
			result++;
	}

	return result;
}



/********************************************************/
/************* Memory cleaners & handlers ***************/
/********************************************************/

void int_free_and_null (int **ptr)
{
   if ( *ptr != NULL ) {
      free (*ptr);
      *ptr = NULL;
   }
}

void char_free_and_null (char **ptr)
{
   if ( *ptr != NULL ) {
      free (*ptr);
      *ptr = NULL;
   }
}

void double_free_and_null (double **ptr)
{
   if ( *ptr != NULL ) {
      free (*ptr);
      *ptr = NULL;
   }
}

int int_allocation_verifier(int* ptr){
	if(ptr == NULL){
		perror("Warning: standard function malloc has failed. Exiting program...\n");
		return 0;
	}
	return 1;
}

int char_allocation_verifier(char* ptr){
	if(ptr == NULL){
			perror("Warning: standard function malloc has failed. Exiting program...\n");
			return 0;
		}
	return 1;
}

int double_allocation_verifier(double* ptr){
	if(ptr == NULL){
			perror("Warning: standard function malloc has failed. Exiting program...\n");
			return 0;
		}
	return 1;
}

void free_null_constraint_memory(double *obj,char *sense, double *rhs, int *matbeg, int *matcnt,
		int *matind, double *matval, int *indices, char *ctype, double *lp, double *up, double *x,
		char *probname){

	double_free_and_null(&obj);
	int_free_and_null(&matbeg);
	int_free_and_null(&matcnt);
	int_free_and_null(&matind);
	double_free_and_null(&matval);
	char_free_and_null(&sense);
	double_free_and_null(&rhs);
	int_free_and_null(&indices);
	char_free_and_null(&ctype);
	double_free_and_null(&lp);
	double_free_and_null(&up);
	double_free_and_null(&x);
	char_free_and_null(&probname);

}

